home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Xconq 7.0d37 / source / kernel / history.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-25  |  9.5 KB  |  420 lines  |  [TEXT/KAHL]

  1. /* Management of historical information about an Xconq game.
  2.    Copyright (C) 1992, 1993, 1994, 1995 Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. #include "conq.h"
  10.  
  11. static void rewrite_unit_references PROTO ((int oldid, int newid));
  12. static void rewrite_unit_references_in_event PROTO ((HistEvent *hevt, int oldid, int newid));
  13.  
  14. HevtDefn hevtdefns[] = {
  15.  
  16. #undef  DEF_HEVT
  17. #define DEF_HEVT(NAME, code, DATADESCS) { NAME, DATADESCS },
  18.  
  19. #include "history.def"
  20.  
  21.     { NULL, NULL }
  22. };
  23.  
  24. /* Head of the list of events. */
  25.  
  26. HistEvent *history;
  27.  
  28. /* The list of all past unit records. */
  29.  
  30. PastUnit *past_unit_list;
  31.  
  32. /* The id of the next past unit to be synthesized.  -1 is reserved, so
  33.    start counting down from -2. */
  34.  
  35. int next_past_unit_id = -2;
  36.  
  37. /* Buffers for descriptions of past units. */
  38.  
  39. #define NUMPASTBUFS 3
  40.  
  41. int curpastbuf = 0;
  42.  
  43. char *pastbufs[NUMPASTBUFS] = { NULL, NULL, NULL };
  44.  
  45. /* True if events are being recorded into the history. */
  46.  
  47. int recording_events;
  48.  
  49. void
  50. init_history()
  51. {
  52.     /* The first "event" is just a marker. */
  53.     history = create_historical_event(0);
  54.     /* Give it an impossible date. */
  55.     history->startdate = -1;
  56.     history->next = history->prev = history;
  57.     /* This event cannot have any observers, since it occurs
  58.        before the beginning of the game. */
  59.     /* (should do later perhaps?) */
  60.     recording_events = TRUE;
  61.     record_event(H_LOG_STARTED, NOSIDES);
  62.     /* Initialize the past unit record. */
  63.     past_unit_list = NULL;
  64.     next_past_unit_id = -2;
  65. }
  66.  
  67. HistEvent *
  68. create_historical_event(type)
  69. HistEventType type;
  70. {
  71.     HistEvent *hevt = (HistEvent *) xmalloc(sizeof(HistEvent));
  72.  
  73.     if (!between(0, type, NUMHEVTTYPES))
  74.       run_warning("unknown hist event type %d", type);
  75.     hevt->type = type;
  76.     hevt->observers = ALLSIDES;
  77.     hevt->next = hevt->prev = NULL;
  78.     return hevt;
  79. }
  80.  
  81. HistEvent *
  82. #ifdef __STDC__
  83. record_event(HistEventType type, SideMask observers, ...)
  84. #else
  85. record_event(type, observers, d1, d2, d3, d4)
  86. HistEventType type;
  87. SideMask observers;
  88. long d1, d2, d3, d4;
  89. #endif
  90. {
  91.     int i, val;
  92.     char *descs;
  93.     HistEvent *hevt;
  94.     Side *side;
  95.  
  96.     if (!recording_events)
  97.       return NULL;
  98.     hevt = create_historical_event(type);
  99.     hevt->startdate = g_turn();
  100.     /* (should set sequence number also?) */
  101.     hevt->enddate = g_turn();
  102.     hevt->observers = observers;
  103.     descs = hevtdefns[type].datadescs;
  104. #ifdef __STDC__
  105.     {
  106.     va_list ap;
  107.  
  108.     va_start(ap, observers);
  109.     for (i = 0; descs[i] != '\0'; ++i) {
  110.         val = va_arg(ap, long);
  111.         switch (descs[i]) {
  112.           case 'S':
  113.             if (!between(0, val, numsides))
  114.               run_warning("invalid side number %d in hist event", val);
  115.             break;
  116.           case 'U':
  117.             /* (should check unit validity?) */
  118.             break;
  119.         }
  120.         hevt->data[i] = val;
  121.     }
  122.     va_end(ap);
  123.     }
  124. #else
  125.     hevt->data[0] = d1;
  126.     hevt->data[1] = d2;
  127.     hevt->data[2] = d3;
  128.     hevt->data[3] = d4;
  129. #endif
  130.     /* Insert the newly created event. */
  131.     hevt->next = history;
  132.     hevt->prev = history->prev;
  133.     history->prev->next = hevt;
  134.     history->prev = hevt;
  135.     Dprintf("Recorded event %s (observed by %d)\n",
  136.         hevtdefns[hevt->type].name, hevt->observers);
  137.     if (observers != NOSIDES) {
  138.     /* Let all the observers' interfaces look at this event. */
  139.     for_all_sides(side) {
  140.         if (side_in_set(side, observers)) {
  141.         update_event_display(side, hevt, TRUE);
  142.         }
  143.     }
  144.     }
  145.     if (any_post_event_scores)
  146.       check_post_event_scores(hevt);
  147.     return hevt;
  148. }
  149.  
  150. void
  151. record_unit_loss(unit, reason)
  152. Unit *unit;
  153. int reason;
  154. {
  155.     int lossreason;
  156.     HistEvent *hevt;
  157.     PastUnit *pastunit;
  158.  
  159.     pastunit = create_past_unit(unit->type, 0);
  160.     pastunit->name = unit->name;
  161.     pastunit->number = unit->number;
  162.     pastunit->x = unit->x;  pastunit->y = unit->y;  pastunit->z = unit->z;
  163.     pastunit->side = unit->side;
  164.     rewrite_unit_references(unit->id, pastunit->id);
  165.     hevt = record_event(reason, add_side_to_set(unit->side, NOSIDES), pastunit->id);
  166.     lossreason = -1;
  167.     if (unit->side) {
  168.     switch (reason) {
  169.       /* (should add all loss reasons here) */
  170.       case H_UNIT_CAPTURED:
  171.         break;
  172.       case H_UNIT_KILLED:
  173.       case H_UNIT_WRECKED:
  174.         lossreason = 0;
  175.         break;
  176.       case H_UNIT_STARVED:
  177.         lossreason = 1;
  178.         break;
  179.       case H_UNIT_DISBANDED:
  180.         lossreason = 2;
  181.         break;
  182.       case H_UNIT_LEFT_WORLD:
  183.         break;
  184.       default:
  185.         break;
  186.     }
  187.     if (lossreason >= 0) {
  188.         ++(unit->side->losscounts[3 * unit->type + lossreason]);
  189.     }
  190.     }
  191. }
  192.  
  193. PastUnit *
  194. change_unit_to_past_unit(unit)
  195. Unit *unit;
  196. {
  197.     PastUnit *pastunit;
  198.  
  199.     pastunit = create_past_unit(unit->type, 0);
  200.     pastunit->name = unit->name;
  201.     pastunit->number = unit->number;
  202.     pastunit->x = unit->x;  pastunit->y = unit->y;  pastunit->z = unit->z;
  203.     pastunit->side = unit->side;
  204.     rewrite_unit_references(unit->id, pastunit->id);
  205.     return pastunit;
  206. }
  207.  
  208. void
  209. record_unit_name_change(unit, newname)
  210. Unit *unit;
  211. char *newname;
  212. {
  213.     PastUnit *pastunit;
  214.  
  215.     pastunit = create_past_unit(unit->type, 0);
  216.     pastunit->name = unit->name;
  217.     pastunit->number = unit->number;
  218.     pastunit->x = unit->x;  pastunit->y = unit->y;  pastunit->z = unit->z;
  219.     pastunit->side = unit->side;
  220.     rewrite_unit_references(unit->id, pastunit->id);
  221.     record_event(H_UNIT_NAME_CHANGED, -1, pastunit->id, unit->id);
  222. }
  223.  
  224. PastUnit *
  225. create_past_unit(type, id)
  226. int type, id;
  227. {
  228.     PastUnit *pastunit = (PastUnit *) xmalloc(sizeof(PastUnit));
  229.  
  230.     pastunit->type = type;
  231.     if (id == 0)
  232.       id = next_past_unit_id--;
  233.     else
  234.       next_past_unit_id = min(next_past_unit_id, id - 1);
  235.     pastunit->id = id;
  236.     pastunit->next = past_unit_list;
  237.     past_unit_list = pastunit;
  238.     return pastunit;
  239. }
  240.  
  241. PastUnit *
  242. find_past_unit(n)
  243. int n;
  244. {
  245.     PastUnit *pastunit;
  246.  
  247.     for (pastunit = past_unit_list; pastunit != NULL; pastunit = pastunit->next) {
  248.     if (pastunit->id == n)
  249.       return pastunit;
  250.     }
  251.     return NULL;
  252. }
  253.  
  254. static void
  255. rewrite_unit_references(oldid, newid)
  256. int oldid, newid;
  257. {
  258.     HistEvent *hevt;
  259.  
  260.     rewrite_unit_references_in_event(history, oldid, newid);
  261.     for (hevt = history->next; hevt != history; hevt = hevt->next) {
  262.     rewrite_unit_references_in_event(hevt, oldid, newid);
  263.     }
  264. }
  265.  
  266. static void
  267. rewrite_unit_references_in_event(hevt, oldid, newid)
  268. HistEvent *hevt;
  269. int oldid, newid;
  270. {
  271.     int i;
  272.     char *descs;
  273.  
  274.     descs = hevtdefns[hevt->type].datadescs;
  275.     /* Scan through the data description, looking for
  276.        values that are references to actual units. */
  277.     for (i = 0; descs[i] != '\0'; ++i) {
  278.     if (descs[i] == 'U' && hevt->data[i] == oldid)
  279.       hevt->data[i] = newid;
  280.     }
  281. }
  282.  
  283. /* Indicate that history is no longer being recorded. */
  284.  
  285. void
  286. end_history()
  287. {
  288.     record_event(H_LOG_ENDED, ALLSIDES);
  289.     recording_events = FALSE;
  290. }
  291.  
  292. HistEvent *
  293. get_nth_history_line(side, n, nextevt)
  294. Side *side;
  295. int n;
  296. HistEvent **nextevt;
  297. {
  298.     int i = 0;
  299.     HistEvent *hevt;
  300.     
  301.     for (hevt = history->next; hevt != history; hevt = hevt->next) {
  302.     if (side_in_set(side, hevt->observers)) {
  303.         if (n == 0) {
  304.         *nextevt = hevt;
  305.         return NULL;
  306.         }
  307.         if (i == n) {
  308.         *nextevt = hevt->next;
  309.         return hevt;
  310.         }
  311.         if (hevt->startdate != hevt->prev->startdate) {
  312.         ++i;
  313.         if (i == n) {
  314.             *nextevt = hevt->next;
  315.             return NULL;
  316.         }
  317.         }
  318.         ++i;
  319.     }
  320.     }
  321.     /* Return the last event. */
  322.     *nextevt = history;
  323.     return history->prev;
  324. }
  325.  
  326. /* Summarize various aspects of performance in the game, writing it
  327.    all to a file. */
  328.  
  329. void
  330. dump_statistics()
  331. {
  332.     Side *side;
  333.     FILE *fp;
  334.  
  335.     fp = fopen(statistics_filename(), "w");
  336.     if (fp != NULL) {
  337.     if (1 /* records exist */) {
  338.         write_side_results(fp, NULL);
  339.         write_unit_record(fp, NULL);
  340.         write_combat_results(fp, NULL);
  341.         fprintf(fp, "\f\n");
  342.         for_all_sides(side) {
  343.         write_side_results(fp, side);
  344.         write_unit_record(fp, side);
  345.         write_combat_results(fp, side);
  346.         if (side->next != NULL)
  347.           fprintf(fp, "\f\n");
  348.         }
  349.     } else {
  350.         fprintf(fp, "No statistics were kept.\n");
  351.     }
  352.     fclose(fp);
  353.     } else {
  354.     run_warning("Can't open statistics file \"%s\"",
  355.             statistics_filename());
  356.     }
  357. }
  358.  
  359. /* Short, unreadable, but greppable listing of past unit.  Primarily useful
  360.    for debugging and warnings.  We use several buffers and rotate between
  361.    them so we can call this more than once in a single printf. */
  362.  
  363. char *
  364. past_unit_desig(pastunit)
  365. PastUnit *pastunit;
  366. {
  367.     char *shortbuf;
  368.  
  369.     if (pastunit == NULL)
  370.       return "no pastunit";
  371.     /* Allocate if not yet done so. */
  372.     if (pastbufs[curpastbuf] == NULL)
  373.       pastbufs[curpastbuf] = xmalloc(BUFSIZE);
  374.     shortbuf = pastbufs[curpastbuf];
  375.     curpastbuf = (curpastbuf + 1) % NUMPASTBUFS;
  376.     if (pastunit->id == -1) {
  377.     sprintf(shortbuf, "s%d head", side_number(pastunit->side));
  378.     return shortbuf;
  379.     } else if (is_unit_type(pastunit->type)) {
  380.     sprintf(shortbuf, "s%d %-3.3s %d (%d,%d",
  381.         side_number(pastunit->side),
  382.         shortest_unique_name(pastunit->type),
  383.         pastunit->id, pastunit->x, pastunit->y);
  384.     if (pastunit->z != 0)
  385.       tprintf(shortbuf, ",%d", pastunit->z);
  386.     strcat(shortbuf, ")");  /* close out the pastunit location */
  387.     return shortbuf;
  388.     } else {
  389.     return "!garbage pastunit!";
  390.     }
  391. }
  392.  
  393. /* Would be faster to stash these, but enough difference to care? */
  394.  
  395. int
  396. total_gain(side, u)
  397. Side *side;
  398. int u;
  399. {
  400.     int i, total = 0;
  401.  
  402.     for (i = 0; i < 3; ++i)
  403.       total += side_gain_count(side, u, i);
  404.     return total;
  405. }
  406.  
  407. int
  408. total_loss(side, u)
  409. Side *side;
  410. int u;
  411. {
  412.     int i, total = 0;
  413.  
  414.     for (i = 0; i < 3; ++i)
  415.       total += side_loss_count(side, u, i);
  416.     return total;
  417. }
  418.  
  419.  
  420.